﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using HardStyle.SimpleEventBus.Internal;

namespace HardStyle.SimpleEventBus
{
    public class SimpleEventBus : ISimpleEventBus
    {
        private static readonly ConcurrentDictionary<Type, SimpleEventHandlerWrapper> _eventHandlerWrappers = new ConcurrentDictionary<Type, SimpleEventHandlerWrapper>();
        private readonly ServiceFactory _serviceFactory;
        public SimpleEventBus(ServiceFactory serviceFactory) =>
            _serviceFactory = serviceFactory;


        public Task Publish(object eventArg, CancellationToken cancellationToken = default) =>
            eventArg switch
            {
                null => throw new ArgumentNullException(nameof(eventArg)),
                ISimpleEvent simpleEvent => Publish(simpleEvent, cancellationToken),
                _ => throw new ArgumentException($"{nameof(eventArg)} does not implement ${nameof(ISimpleEvent)}")
            };


        public Task Publish<TEvent>(TEvent eventArg, CancellationToken cancellationToken = default) where TEvent : ISimpleEvent
        {
            if (eventArg is null)
            {
                throw new ArgumentNullException(nameof(eventArg));
            }
            return PublishEvent(eventArg, cancellationToken);
        }


        private Task PublishEvent(ISimpleEvent eventArg, CancellationToken cancellationToken = default)
        {
            var eventType = eventArg.GetType();
            var handler = _eventHandlerWrappers.GetOrAdd(eventType, t => (SimpleEventHandlerWrapper)Activator.CreateInstance(typeof(SimpleEventHandlerWrapperImp<>).MakeGenericType(t)) ??
             throw new InvalidOperationException($"Could not create wrapper for type {t}"));

            return handler.Handle(eventArg, cancellationToken, _serviceFactory, PublishCore);
        }


        protected virtual async Task PublishCore(IEnumerable<Func<ISimpleEvent, CancellationToken, Task>> allHandlers, ISimpleEvent notification, CancellationToken cancellationToken)
        {
            foreach (var handler in allHandlers)
            {
                await handler(notification, cancellationToken).ConfigureAwait(false);
            }
        }









    }
}
